#include "../header/fudpClient.hpp"

fudpClient::fudpClient(const char* _ip){
    MLOG("init a client to"<<string(_ip));
    this->dst_ip = _ip;
    initSocket(fltp_port);
    MLOG("finish init fudp client");
}

bool fudpClient::lsend(const char* filename){
    MLOG("into lsend\n");
    handleHandshake(method_send,filename);

    // begin to send
    FUDP_Sender sender(fd, dst_address,filename);
    
    sender.send();

    this->handleClose();
}

void fudpClient::handleHandshake(const char* method, const char* filename) {
    MLOG("CLIENT ask request");
    // temp buffer
    char buffer[max_datagram_size]={0};
    // try to ask request
    strcpy(buffer,filename);

    strcpy(buffer,method);
    strcpy(buffer+10,filename);
    Datagram clientRequestDatagram(type_clientAsk);
    if(!clientRequestDatagram.fillData(buffer,10+strlen(filename),0)) {
        MLOG("file name error");
        exit(1);
    }
    // MLOG("header: "<<clientRequestDatagram.getHeader().dataLength);
    clientRequestDatagram.getData(buffer);
    // MLOG("header: "<<clientRequestDatagram.getHeader().dataLength);
    MLOG("request for"<<buffer);

    int rsize = clientRequestDatagram.get(buffer);
    if(sendto(fd,buffer,rsize,0,(struct sockaddr*)&dst_address,sizeof(dst_address))<0){
        MLOG("Client could no send request");
        exit(1);
    }

    // try to wait server ack and ask
    // reset the dst_address to the response's address
    socklen_t len=0;
    if(recvfrom(fd,buffer,max_datagram_size-1,0,(struct sockaddr*)&dst_address,&len)<0){
        MLOG("Client can not get server's response");
        exit(1);
    } else {
        Datagram data(buffer);
        int newPort = atoi(buffer+header_size);
        MLOG("NEW PORT"<<newPort);
        close(fd);
        initSocket(newPort);

        // ack ready
        Datagram clientAckReadyDatagram(type_clientACKReady);
        rsize = clientAckReadyDatagram.get(buffer);
        if(sendto(fd,buffer,rsize,0,(struct sockaddr*)&dst_address,sizeof(dst_address))<0){
            MLOG("Client could no send ack ready");
            exit(1);
        }
    }
}

void fudpClient::handleClose(){
    MLOG("Client close request");
    char buffer[max_datagram_size]={0};
    int rsize=0;
    Datagram fin(type_clientFinish);
    rsize = fin.get(buffer);
    if(sendto(fd,buffer,rsize,0,(struct sockaddr*)&dst_address,sizeof(dst_address))<0){
        MLOG("Client could no fin");
        exit(1);
    }
}

bool fudpClient::lget(const char* filename){
    MLOG("into get");
    handleHandshake(method_get,filename);
    // begin to get
    fudpReciver reciver(fd, dst_address,filename);
    reciver.recieve();
    
    this->handleClose();
}

int fudpClient::initSocket(int port){
    const char* ip = this->dst_ip;
    fd = socket(PF_INET,SOCK_DGRAM , 0);
    assert(fd >= 0);
    struct sockaddr_in address;
    bzero(&address,sizeof(address));
    //转换成网络地址
    address.sin_port = htons(port);
    address.sin_family = AF_INET;
    //地址转换
    // inet_pton(AF_INET, ip, &address.sin_addr);
    address.sin_addr.s_addr = htonl(INADDR_ANY);
    int check_ret = bind(fd,(struct sockaddr*)&address,sizeof(address));
    MLOG("check ret:"<<check_ret);

    struct timeval timeout={CLIENT_MAX_RTT};
    //即timeout={4,0}; 或者timeout.tv_sec=4; timeout.tv_usec=0;
    //设置接收超时
    //setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(timeout));
    setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(struct timeval));
    
    bzero(&dst_address,sizeof(dst_address));
    dst_address.sin_port = htons(port);
    dst_address.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &dst_address.sin_addr);
}